/*****************************************************************************
Copyright 2006 Kervin L. Pierre
    EMAIL: otlkcon@adevsoft.com

This file is part of Otlkcon.

Otlkcon is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

Otlkcon is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Lesser GNU General Public License for more details.

You should have received a copy of the Lesser GNU General Public License
along with Otlkcon; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*******************************************************************************/

#include "StdAfx.h"
#include "O_IContainer.h"
#include "O_IFolder.h"
#include "otlkcon_generic_msgstore.h"

extern map< O_ENTRYID *, void *, otlkcon_entryid_less >      otlkcon_event_objects;
extern CRITICAL_SECTION    otlkcon_objects_cs;
extern CRITICAL_SECTION    otlkcon_sqlite_cs;
extern HWND  otlkcon_eventHwnd;

extern map< O_ENTRYID *, O_IContainer *, otlkcon_entryid_less > otlkcon_xpp_wait_set;
extern CRITICAL_SECTION otlkcon_xpp_wait_set_cs;

bool operator <(const O_ENTRYID u1, const O_ENTRYID u2)
{
	return 0 < memcmp(&u1.instanceGuid, &u2.instanceGuid, sizeof(MAPIUID));
}

O_IContainer::O_IContainer(void)
{
    hierarchyTable = NULL;
    contentsTable  = NULL;
    assocContentsTable = NULL;
    msgstore = NULL;
    objectCreateConnection = 0;
    xpAccountEID = NULL;
    adviseSink = new O_IContainerAdviseSink( this );
    isForeignView = 0;
}

O_IContainer::~O_IContainer(void)
{
    delete adviseSink;
}

HRESULT O_IContainer::Create( otlkcon_generic_msgstore *mstr, MAPIUID ig, MAPIUID tg )
{
    HRESULT result        = S_OK;
    ULONG propCount = 0;

    SPropValue defaultProps[1] = {0};
    SizedSPropTagArray(30, hierProps);
    SizedSPropTagArray(64, contProps);

    msgstore = mstr;

    result = O_IProp::Create(mstr, ig, tg );
    if( result != S_OK )
        return result;

    entryID->supportedInterfaces |= OTLKCON_IMAPICONTAINER_CLASS;
    entryID->storeInstanceGuid = msgstore->entryID->storeInstanceGuid;

    propCount = 0;
    hierProps.aulPropTag[propCount++] = PR_INSTANCE_KEY;
	hierProps.aulPropTag[propCount++] = PR_ENTRYID;
	hierProps.aulPropTag[propCount++] = PR_RECORD_KEY;
    hierProps.aulPropTag[propCount++] = PR_COMMENT;
    hierProps.aulPropTag[propCount++] = PR_DISPLAY_NAME;
    hierProps.aulPropTag[propCount++] = PR_DISPLAY_TYPE;
    hierProps.aulPropTag[propCount++] = PR_OBJECT_TYPE;
    hierProps.aulPropTag[propCount++] = PR_DEPTH;
    hierProps.aulPropTag[propCount++] = PR_STATUS;
    hierProps.aulPropTag[propCount++] = PR_FOLDER_TYPE;
    hierProps.aulPropTag[propCount++] = PR_SUBFOLDERS;
    hierProps.aulPropTag[propCount++] = PR_CONTENT_COUNT;
    hierProps.aulPropTag[propCount++] = PR_CONTENT_UNREAD;
    hierProps.aulPropTag[propCount++] = PR_ACCESS;
    hierProps.aulPropTag[propCount++] = PR_PARENT_ENTRYID;
    hierProps.aulPropTag[propCount++] = PR_STORE_ENTRYID;
    hierProps.aulPropTag[propCount++] = PR_STORE_RECORD_KEY;
    hierProps.aulPropTag[propCount++] = PR_MAPPING_SIGNATURE;
    hierProps.aulPropTag[propCount++] = PR_CONTAINER_CLASS;
    hierProps.aulPropTag[propCount++] = PR_NULL;
    hierProps.aulPropTag[propCount++] = PR_NULL;
    hierProps.aulPropTag[propCount++] = PR_NULL;
    hierProps.aulPropTag[propCount++] = PR_NULL;
    hierProps.aulPropTag[propCount++] = PR_NULL;
    hierProps.cValues = propCount;

    propCount = 0;
    contProps.aulPropTag[propCount++] = PR_INSTANCE_KEY;
	contProps.aulPropTag[propCount++] = PR_ENTRYID;
	contProps.aulPropTag[propCount++] = PR_RECORD_KEY;
    contProps.aulPropTag[propCount++] = PR_ACCESS_LEVEL;
    contProps.aulPropTag[propCount++] = PR_PRIORITY;
    contProps.aulPropTag[propCount++] = PR_DISPLAY_CC;
    contProps.aulPropTag[propCount++] = PR_DISPLAY_TO;
    contProps.aulPropTag[propCount++] = PR_HASATTACH;
    contProps.aulPropTag[propCount++] = PR_LAST_MODIFICATION_TIME;
    contProps.aulPropTag[propCount++] = PR_MAPPING_SIGNATURE;
    contProps.aulPropTag[propCount++] = PR_MESSAGE_CLASS;
    contProps.aulPropTag[propCount++] = PR_MESSAGE_FLAGS;
    contProps.aulPropTag[propCount++] = PR_MESSAGE_SIZE;
    contProps.aulPropTag[propCount++] = PR_MSG_STATUS;
    contProps.aulPropTag[propCount++] = PR_OBJECT_TYPE;
    contProps.aulPropTag[propCount++] = PR_PARENT_ENTRYID;
    contProps.aulPropTag[propCount++] = PR_STORE_ENTRYID;
    contProps.aulPropTag[propCount++] = PR_STORE_RECORD_KEY;
    contProps.aulPropTag[propCount++] = PR_CONTENT_COUNT;
    contProps.aulPropTag[propCount++] = PR_SUBJECT;
    contProps.aulPropTag[propCount++] = PR_OTLKCON_FOREIGN_ID;
    contProps.aulPropTag[propCount++] = PR_OTLKCON_FOREIGN_HASH;
    contProps.aulPropTag[propCount++] = PR_OTLKCON_UPLOAD;
	contProps.aulPropTag[propCount++] = PR_OTLKCON_DOWNLOAD;
    contProps.aulPropTag[propCount++] = PR_OTLKCON_PENDING_ACTIONS;

    //Reserve spaces for 'k' new columns
    for(int k=0; k<30; k++)
    {
        contProps.aulPropTag[propCount++] = PR_NULL;
    }

    contProps.cValues = propCount;

    // Set a few default properties
    //New IMAPIContainer, therefore create properties
    
    // PR_CONTAINER_HIERARCHY
    // Hierarchy table of child container objects
    result = OpenProperty(PR_CONTAINER_HIERARCHY, &IID_IMAPITable, 0, 
                    MAPI_CREATE|MAPI_MODIFY, (LPUNKNOWN *)&hierarchyTable);
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IContainer::Create() failed :  CreateTable() failed");
        OTLKCON_DEBUGBREAK
        return E_FAIL;
    }
    result = hierarchyTable->Init( (LPSPropTagArray)&hierProps );
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IContainer::Create() failed :  CreateTable() failed");
        OTLKCON_DEBUGBREAK
        return E_FAIL;
    }
    hierarchyTable->objName = memObj->_strdup("hier");

    //PR_CONTAINER_CONTENTS
    result = OpenProperty(PR_CONTAINER_CONTENTS, &IID_IMAPITable, 0, 
                    MAPI_CREATE|MAPI_MODIFY, (LPUNKNOWN *)&contentsTable);
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IContainer::Create() failed :  CreateTable() failed");
        OTLKCON_DEBUGBREAK
        return E_FAIL;
    }
    result = contentsTable->Init( (LPSPropTagArray)&contProps );
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IContainer::Create() failed :  CreateTable() failed");
        OTLKCON_DEBUGBREAK
        return E_FAIL;
    }
    contentsTable->objName = memObj->_strdup("contents");

    return result;
}

HRESULT O_IContainer::Load( otlkcon_generic_msgstore *mstr, O_ENTRYID *eid )
{
    HRESULT result = S_OK;

    result = O_IProp::Load( mstr, eid );
    if( result != S_OK )
    {
        return result;
    }

    result = OpenProperty(PR_CONTAINER_HIERARCHY, &IID_IMAPITable, 0, 
                    MAPI_MODIFY, (LPUNKNOWN *)&hierarchyTable);
    hierarchyTable->objName = memObj->_strdup("hier");
	if(SUCCEEDED(result))
	{
		result = OpenProperty(PR_CONTAINER_CONTENTS, &IID_IMAPITable, 0, 
			MAPI_MODIFY, (LPUNKNOWN *)&contentsTable);
		contentsTable->objName = memObj->_strdup("contents");

	} // if
    return result;
}

void O_IContainer::Destroy()
{
    ;
}

STDMETHODIMP O_IContainer::GetContentsTable(ULONG ulFlags, LPMAPITABLE *lppTable)
{
    HRESULT result = S_OK;

    Validate_IMAPIContainer_GetContentsTable(this,ulFlags,lppTable);

    lastCalledFunctionID = OTLKCON_ICONTAINER_GETCONTENTSTABLE;
    strncpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_ICONTAINER_GETCONTENTSTABLE_STR,
        OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE);
    result = SetLastError(S_OK, "");

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"inside IContainer->GetContentsTable.\n");

    if( lppTable )
    {
        if( ulFlags & MAPI_ASSOCIATED )
        {
            if( assocContentsTable )
            {
                result 
                    = assocContentsTable->HrGetView( NULL, NULL, NULL, lppTable );
            }
            else
            {
                // This container does not have an associated contents table
                OTLKCON_DEBUGBREAK;
                result = E_FAIL;
            }
        }
        else
        {
            //O_ITable *tbl;
            // MAPI frees this memory
            result = contentsTable->HrGetView( NULL, NULL, NULL, lppTable );
            //result = contentsTable->Duplicate( &tbl );
            //if( result != S_OK )
            //{
            //    OTLKCON_DEBUGBREAK;
            //}
            //*lppTable = (LPMAPITABLE)tbl;
        }
    }
    else
    {
        OTLKCON_DEBUGBREAK;
    }

    return result;
}

STDMETHODIMP O_IContainer::GetHierarchyTable(ULONG ulFlags, LPMAPITABLE *lppTable)
{
    HRESULT result = S_OK;

    Validate_IMAPIContainer_GetHierarchyTable(this,ulFlags,lppTable);

    lastCalledFunctionID = OTLKCON_ICONTAINER_GETHIERARCHYTABLE;
    strncpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_ICONTAINER_GETHIERARCHYTABLE_STR,
        OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE);
    result = SetLastError(S_OK, "");

    if( lppTable )
    {
        // MAPI frees this memory
        result = hierarchyTable->HrGetView(NULL, NULL, NULL, lppTable);
        //O_ITable *tbl;
        //result = hierarchyTable->Duplicate( &tbl );
        //if( result != S_OK )
        //{
        //    OTLKCON_DEBUGBREAK;
        //}
        //*lppTable = (LPMAPITABLE)tbl;
    }
    else
    {
        OTLKCON_DEBUGBREAK;
    }

    return result;
}

STDMETHODIMP O_IContainer::OpenEntry(ULONG cbEntryID,
  LPENTRYID lpEntryID,
  LPCIID lpInterface,
  ULONG ulFlags,
  ULONG *lpulObjType,
  LPUNKNOWN *lppUnk 
)
{
    HRESULT result, result1;
    O_ENTRYID *eid;

    //Validate_IMAPIContainer_OpenEntry(this,cbEntryID,lpEntryID,lpInterface,
     //                       ulFlags,lpulObjType,lppUnk);

    lastCalledFunctionID = OTLKCON_ICONTAINER_OPENENTRY;
    strncpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_ICONTAINER_OPENENTRY_STR,
        OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE);
    result = SetLastError(S_OK, "");

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"inside O_IContainer->OpenEntry.\n");

    if( lpEntryID == NULL )
    {
        // FIXME:  This is incorrect.  We need to support
        //         other message classes, etc.
        *lppUnk = msgstore->topLevelFolders["IPM"];
        *lpulObjType = MAPI_FOLDER;

        return S_OK;
    }

    if( cbEntryID != sizeof(O_ENTRYID) )
    {
        OTLKCON_DEBUGBREAK;
        return E_FAIL;
    }

    eid = (O_ENTRYID*)lpEntryID;

    SRestriction rt = {0};
    SPropValue prop = {0};
    prop.ulPropTag  = PR_ENTRYID;
    prop.Value.bin.lpb  = (LPBYTE)eid;
    prop.Value.bin.cb   = sizeof(O_ENTRYID);
    rt.rt = RES_PROPERTY;
    rt.res.resProperty.ulPropTag = PR_ENTRYID;
    rt.res.resProperty.relop     = RELOP_EQ;
    rt.res.resProperty.lpProp    = &prop;

    if( eid->typeID == MAPI_FOLDER )
    {
        // Search the hierarchy table
        //Dump();
        result = hierarchyTable->FindRow(&rt, BOOKMARK_BEGINNING, 0);
        if( result != S_OK )
        {
            // The object is not in the hierarchy table
            return MAPI_E_NOT_FOUND;
        }
    }
    else if( eid->typeID == MAPI_MESSAGE )
    {
        // Search contents table for the object
        result = contentsTable->FindRow(&rt, BOOKMARK_BEGINNING, 0);
        result1 = MAPI_E_NOT_FOUND;
        if( assocContentsTable )
        {
            result1 = assocContentsTable->FindRow(&rt, BOOKMARK_BEGINNING, 0);
        }

        if( result != S_OK && result1 != S_OK )
        {
            return MAPI_E_NOT_FOUND;
        }
    }
    else
    {
        OTLKCON_DEBUGBREAK;
    }

    // The object was found in one of our tables
    result = msgstore->OpenEntry(cbEntryID, lpEntryID, lpInterface,
                                    ulFlags, lpulObjType, lppUnk);

    return result;
}

HRESULT O_IContainer::ProcessEvent( otlkcon_event_struct *ev )
{
    HRESULT result = S_OK;

    switch( ev->eventType )
    {
        case OTLKCON_EVENT_GET_ID_RES:
            // Update the backend entryID here
            result = OnGetIdRes(ev);
            break;
		case OTLKCON_EVENT_UPLOAD_ICAL_RES:
			//Update Etag for uploaded message
			result = OnUploadRes(ev);
			break;
        default:
            OTLKCON_DEBUGBREAK;
            break;
    }

    return result;
}

HRESULT O_IContainer::OnGetIdRes( otlkcon_event_struct *ev )
{
    HRESULT result = S_OK;
    O_ENTRYID *tmpEid = NULL;

    if( ev == NULL )
    {
        OTLKCON_DEBUGBREAK;
        return E_FAIL;
    }

    if( ev->cbData < 1 )
    {
        //  No entryID returned.
        return S_OK;
    }

	OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IContainer::OnGetIdRes");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> O_IContainer::OnGetIdRes");

    // FIXME: The first value should be a count, then the
    //        actual data should follow.
    tmpEid = (O_ENTRYID *)(ev->data);
    result = msgstore->ValidateEntryID(tmpEid, sizeof(O_ENTRYID), 0, 0);
    if( result != S_OK )
    {
       // OTLKCON_DEBUGBREAK;
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_ALL,  OTLKCON_LOG_TARGET_ALL,
            L"ERROR: **** Invalid EntryID ****\n" );
    }

    // FIXME: Could we ever ask for an id that's not
    //        our backends?
    xpAccountEID = tmpEid;

    // FIXME: Find all messages waiting to be added/modified/deleted and
    //        dispatch them.  Search the contents table.
    result = ProcessRemoteMessagesPending(0);
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

//ongetidres_end:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> O_IContainer::OnGetIdRes");
	OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IContainer::OnGetIdRes");

    return result;
}

HRESULT O_IContainer::ProcessRemoteMessagesPending(ULONG ulFlags)
{
    HRESULT result = S_OK;
    O_ENTRYID *tmpEid = NULL;
    ULONG objType, flags=0, ulRows, ulActionFlags;
    O_IMessage *msg = NULL;
    O_ITable *tempTable = NULL;
    LPSRowSet rows = NULL;
    SPropValue tmpProp = {0};

    SizedSPropTagArray( 2, rowTags );

    rowTags.cValues    = 2;
    rowTags.aulPropTag[0] = PR_ENTRYID;
    rowTags.aulPropTag[1] = PR_OTLKCON_PENDING_ACTIONS;

    SRestriction rt = {0};

    rt.rt = RES_EXIST;
    rt.res.resExist.ulPropTag = PR_OTLKCON_PENDING_ACTIONS;

	OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IContainer::ProcessRemoteMessagesPending");
	OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> O_IContainer::ProcessRemoteMessagesPending");
    
	// FIXME: This should select between the contents and associated
    //        contents tables.
    tempTable = contentsTable;

    result = tempTable->Restrict( &rt, 0 );
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

    result = tempTable->GetRowCount( 0, &ulRows );
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        return result;
    }

    if( ulRows < 1 )
    {
        // Empty table
        result = S_OK;
        goto processremotemessagespending_end;
    }

    result = tempTable->QueryRows(ulRows, 0, &rows);
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }
    
    for( ULONG i = 0; i < rows->cRows; i++ )
    {
        // Find the entry id
        tmpEid = NULL;
        for( ULONG j = 0; j < rows->aRow[i].cValues; j++ )
        {
            if( rows->aRow[i].lpProps[j].ulPropTag == PR_ENTRYID )
            {
                tmpEid = (O_ENTRYID*)(rows->aRow[i].lpProps[j].Value.bin.lpb);
                break;
            }
        }

        // Find the action flag
        ulActionFlags = 0;
        for( ULONG j = 0; j < rows->aRow[i].cValues; j++ )
        {
            if( rows->aRow[i].lpProps[j].ulPropTag == PR_OTLKCON_PENDING_ACTIONS )
            {
                ulActionFlags = rows->aRow[i].lpProps[j].Value.l;
                break;
            }
        }

        if( ulActionFlags == OTLKCON_ACTION_NONE )
        {
            // Nothing to do...
            continue;
        }

        if( tmpEid == NULL )
        {
            // Entry id was not found
            OTLKCON_DEBUGBREAK;
            continue;
        }

        result = msgstore->OpenEntry(sizeof(O_ENTRYID), (LPENTRYID)tmpEid, 
                                    &IID_IMessage, 0, &objType, (LPUNKNOWN*)&msg);
		if( result != S_OK )
		{
			OTLKCON_DEBUGBREAK;
		}

        if( ulActionFlags & OTLKCON_ACTION_ADD )
        {
            // Upload the message
            result = UpdateRemoteMessage(msg,OTLKCON_ACTION_ADD);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
            }
		}
        else if( ulActionFlags & OTLKCON_ACTION_MOD )
        {
            // Modify the remote message
            result = UpdateRemoteMessage(msg,OTLKCON_ACTION_MOD);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
            }
        }
        else if( ulActionFlags & OTLKCON_ACTION_DEL )
        {
            // Delete the remote message
            result = DeleteRemoteMessage(msg,OTLKCON_ACTION_DEL);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
            }

            // Remove the object
            HRESULT res=0;
            result = msgstore->ObjectExists( msg->entryID, &res );
            if( res )
            {
                result = msgstore->RemoveObject( msg->entryID, entryID );
                if( result != S_OK )
                {
                    OTLKCON_DEBUGBREAK;
                }
            }
        }

        if( !(ulActionFlags & OTLKCON_ACTION_DEL) )
        {
            // Reset action flag
			//complete deleting is in OnUploadRes
            tmpProp.ulPropTag = PR_OTLKCON_PENDING_ACTIONS;
            tmpProp.Value.l   = OTLKCON_ACTION_NONE;

            result = msg->_IntSetProps(1, (LPSPropValue)&tmpProp, NULL );
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
            }
            
			//we cant call SaveChanges because it will call UploadRemoteMessage in the notification handler
			//so we just modify table
			//result = msg->SaveChanges(0);

            // FIXME: Which table?
            result = contentsTable->HrUpdateObjectRow(0, msg);
        }
    }
	
processremotemessagespending_end:

	if(NULL != rows)
	{
		FreeProws(rows);
	} // if
    
	// Remove current restriction
    result = tempTable->Restrict( NULL, 0 );
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

	OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> O_IContainer::ProcessRemoteMessagesPending");
	OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IContainer::ProcessRemoteMessagesPending");
    return result;
}

STDMETHODIMP O_IContainer::UpdateRemoteMessage(O_IMessage *msg, ULONG flags)
{
    HRESULT result = S_OK;
    char *idStrA = NULL;
    LPSPropValue propResult;
    ULONG count = 0, dataSize = 0;
    otlkcon_event_struct *ev;

        // Send a message to the backend for update via
    // OTLKCON_EVENT_UPLOAD_ICAL

    SizedSPropTagArray(3,tags);
    tags.cValues       = 1;
	tags.aulPropTag[0] = PR_OTLKCON_FOREIGN_ID;

    wchar_t *iCalStr = NULL;
    //result = msg->ICalExport( &iCalStr );
    result = msg->ICalExportMIMEDIR( &iCalStr );
    if( result != S_OK )
    {
        // Error converting to iCal
        OTLKCON_DEBUGBREAK;
        return E_FAIL;
    }

	result = msg->_IntGetProps((LPSPropTagArray)&tags, 0, &count, &propResult);
	switch( result )
	{
		case MAPI_W_ERRORS_RETURNED:
		case S_OK:			

            if( PROP_ID(propResult[0].ulPropTag) == PROP_ID(PR_OTLKCON_FOREIGN_ID) )
            {
                if( PROP_TYPE(propResult[0].ulPropTag) == PT_STRING8 )
                {
                    idStrA = _strdup(propResult[0].Value.lpszA);
                }
                else
                {
                    // Probably PT_ERROR
					idStrA = _strdup("");
//                    OTLKCON_DEBUGBREAK;
                }
            }
			break;

		default:
			//Error condition
            OTLKCON_DEBUGBREAK;
			break;
    }

    if( idStrA == NULL )
    {
        // We don't have an id, not good
        OTLKCON_DEBUGBREAK;
        return E_FAIL;
    }

    wchar_t *idStrW 
            = (wchar_t *)memObj->pMalloc( (strlen(idStrA)+1)*sizeof(wchar_t) );
    swprintf_s(idStrW, strlen(idStrA)+1, L"%S", idStrA);

    dataSize = (ULONG)(wcslen(idStrW)+wcslen(iCalStr)+2)*sizeof(wchar_t)+sizeof(ULONG);
    ev = (otlkcon_event_struct *)memObj->pMalloc( sizeof(otlkcon_event_struct) 
                    + dataSize );
    memcpy(&(ev->senderEntryID), entryID, sizeof(O_ENTRYID));

    memcpy(&(ev->entryID), xpAccountEID, sizeof(O_ENTRYID));
    ev->cbData            =  dataSize;
    memcpy(&(ev->data[0]), &flags, sizeof(ULONG));
    memcpy(&(ev->data[0])+sizeof(ULONG), idStrW, wcslen(idStrW)*sizeof(wchar_t));
    memcpy(&(ev->data[0])+sizeof(ULONG)+(wcslen(idStrW)+1)*sizeof(wchar_t), iCalStr,
                                        wcslen(iCalStr)*sizeof(wchar_t));
    ev->senderHWND = otlkcon_eventHwnd;

    ev->eventType = OTLKCON_EVENT_UPLOAD_ICAL;
    result = otlkcon_event_sendmessage( otlkcon_eventHwnd, otlkcon_eventHwnd, ev );

//    SPropValue tmpProps[1] = {0};
//    tmpProps[0].ulPropTag = PR_OTLKCON_UPLOAD;
//
//    result = msg->_IntSetProps(1, (LPSPropValue)&tmpProps, NULL );
//    if( result != S_OK )
//    {
//        OTLKCON_DEBUGBREAK;
//    }

    return result;
}

HRESULT O_IContainer::OnUploadRes( otlkcon_event_struct *ev )
{
	HRESULT hResult = E_INVALIDARG;
	//TODO find message by ForeignID and update Etag for it
	if(NULL != ev)
	{
		LPWSTR lpForeignID = (LPWSTR)&ev->data[0];
		LPWSTR lpETag = lpForeignID + wcslen(lpForeignID)+1;
		char szForeignID[MAX_PATH+1] = {0};
		_snprintf_s(szForeignID, MAX_PATH+1,
            MAX_PATH, "%S", lpForeignID);
		
		SRestriction sr = {0};
		SPropValue spvForeignID = {0};
		sr.rt = RES_PROPERTY;
		sr.res.resProperty.relop = RELOP_EQ;
		sr.res.resProperty.lpProp = &spvForeignID;
		sr.res.resProperty.ulPropTag = PR_OTLKCON_FOREIGN_ID;
		
		spvForeignID.ulPropTag = PR_OTLKCON_FOREIGN_ID;
		spvForeignID.Value.lpszA = szForeignID;
		
		{ //synhcronized section
			OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IContainer::OnUploadRes");
			OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> O_IContainer::OnUploadRes");
			hResult = contentsTable->FindRow(&sr, BOOKMARK_BEGINNING, 0);
			if(S_OK == hResult) 
			{
				//we found message 
				LPSRowSet lpTargetRow = NULL;
				hResult = contentsTable->QueryRows(1, 0, &lpTargetRow);
				if(SUCCEEDED(hResult) && NULL != lpTargetRow)
				{
					LPSPropValue lpEntryID = PpropFindProp(
						lpTargetRow->aRow[0].lpProps, lpTargetRow->aRow[0].cValues,
						PR_ENTRYID);

					//We found EntryID
					if( NULL != lpEntryID)
					{
						O_ENTRYID *lpMessageID = (O_ENTRYID *)lpEntryID->Value.bin.lpb;
						O_IMessage * lpMessage = NULL;
						ULONG ulObjType = 0;
						hResult = msgstore->OpenEntry(sizeof(O_ENTRYID), (LPENTRYID)lpMessageID, &IID_IMessage, 
							0, &ulObjType, (LPUNKNOWN*)&lpMessage);
						
						if(SUCCEEDED(hResult))
						{
							char szEtag[MAX_PATH+1] = {0};
							_snprintf_s(szEtag, MAX_PATH+1, MAX_PATH,
                                "%S", lpETag);
							
							SPropValue spvEtag = {0};
							spvEtag.ulPropTag = PR_OTLKCON_FOREIGN_HASH;
							spvEtag.Value.lpszA = szEtag;

							// Update Etag for that message
							lpMessage->SetProps(1, &spvEtag, NULL);
							//update pending actions
							SPropTagArray tagPending = {1,{PR_OTLKCON_PENDING_ACTIONS}};
							lpMessage->DeleteProps(&tagPending, NULL);
							//That updates contents table
							lpMessage->SaveChanges(0);
							lpMessage->CompleteUploading();

							lpMessage->Release();	
						} // if
						else if(MAPI_E_NOT_FOUND == hResult)
						{
							//message can be deleted but any way
							OTLKCON_DEBUGBREAK;
						} // else if
						else
						{
							// This isn't good...
							OTLKCON_DEBUGBREAK;
						} // else
					}
					FreeProws(lpTargetRow);
				} // if
				else
				{
					OTLKCON_DEBUGBREAK;
					if(SUCCEEDED(hResult))
					{
						hResult = E_FAIL;
					} // if
				} // else
			} // if
			OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IContainer::OnUploadRes");
			OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> O_IContainer::OnUploadRes");
		}
	} // if
	return hResult;
}

STDMETHODIMP O_IContainer::SetSearchCriteria(LPSRestriction lpRestriction,
  LPENTRYLIST lpContainerList,
  ULONG ulSearchFlags            
)
{
    HRESULT result = MAPI_E_TOO_COMPLEX;

    Validate_IMAPIContainer_SetSearchCriteria(this,lpRestriction,lpContainerList,
                            ulSearchFlags);

    lastCalledFunctionID = OTLKCON_ICONTAINER_SETSEARCHCRITERIA;
    strcpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_ICONTAINER_SETSEARCHCRITERIA_STR);
    SetLastError(MAPI_E_NOT_INITIALIZED, "Search not supported.");

    return result;
}

STDMETHODIMP O_IContainer::GetSearchCriteria(ULONG ulFlags,
  LPSRestriction *lppRestriction,
  LPENTRYLIST *lppContainerList,
  ULONG *lpulSearchState           
)
{
    HRESULT result = MAPI_E_NOT_INITIALIZED;

    Validate_IMAPIContainer_GetSearchCriteria(this,ulFlags,lppRestriction,
                    lppContainerList,lpulSearchState); 

    lastCalledFunctionID = OTLKCON_ICONTAINER_GETSEARCHCRITERIA;
    strcpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_ICONTAINER_GETSEARCHCRITERIA_STR);
    SetLastError(MAPI_E_NOT_INITIALIZED, "Search not supported.");

    return result;
}


HRESULT O_IContainer::GetXPP()
{
    HRESULT result = S_OK;
    LPSPropValue propResult;
    ULONG count = 0, dataSize = 0;
    otlkcon_event_struct *ev;

    SizedSPropTagArray(3,tags);
    tags.cValues       = 1;
	tags.aulPropTag[0] = PR_OTLKCON_FOREIGN_ID;

    // FIXME: We need to force send/receive here if it
    //        hasn't been called yet.  Otherwise there'll
    //        be no transport provider to get.

    // Ask for the backend entryID

    // Send the message to the first transport provider
    // we find.

    // FIXME:  Maybe we should send this message to every
    //         transport provider that we find?
	O_ENTRYID *tmpXPEid = NULL;
	
	{//That is locked context
		
		OTLKCON_ENTERCRITICALSECTION(&otlkcon_objects_cs,
			"O_IContainer::GetXPP");

		for(map< O_ENTRYID *, void *, otlkcon_entryid_less >::iterator i=otlkcon_event_objects.begin();
			i != otlkcon_event_objects.end(); ++i)
		{
            if( (*i).first->primarySupportedInterface 
                    != OTLKCON_IXPPROVIDER_CLASS )
			{
				continue;
			}
			// We've found a XPProvider
			tmpXPEid = (*i).first;
            break;
		}
		//Henry:
		// there is no need to use OTLKCON_LEAVECRITICALSECTION macro (It is an empty macro)
		//Instead of that we need to determine context of lock
		//using braces.
		OTLKCON_LEAVECRITICALSECTION(&otlkcon_objects_cs,
			"O_IContainer::GetXPP");
	
	}//end of locked context
    
	if( tmpXPEid == NULL )
    {
        // We did not find an XPProvider
        // TODO: Store the message and try again later
        return E_FAIL;
    }

    // Get PR_OTLKCON_FOREIGN_ID

    char *idStrA = NULL;
	result = _IntGetProps((LPSPropTagArray)&tags, 0, &count, &propResult);
	switch( result )
	{
		case MAPI_W_ERRORS_RETURNED:
		case S_OK:			
            if( PROP_ID(propResult[0].ulPropTag) == PROP_ID(PR_OTLKCON_FOREIGN_ID) )
            {
                if( PROP_TYPE(propResult[0].ulPropTag) == PT_STRING8 )
                {
                    idStrA = _strdup(propResult[0].Value.lpszA);
                }
                else
                {
                    // Probably PT_ERROR
                    OTLKCON_DEBUGBREAK;
                }
            }
			break;

		default:
			//Error condition
            OTLKCON_DEBUGBREAK;
			break;
    }

    if( idStrA == NULL )
    {
        // We don't have an id, not good
        result = Dump();
        OTLKCON_DEBUGBREAK;
        return E_FAIL;
    }

    dataSize = (ULONG)(strlen(idStrA)+1)*sizeof(wchar_t);
    wchar_t *idStrW 
        = (wchar_t *)memObj->pMalloc( dataSize );
    swprintf_s(idStrW, strlen(idStrA)+1, L"%S", idStrA);

    ev = (otlkcon_event_struct *)memObj->pMalloc( sizeof(otlkcon_event_struct) 
                    + dataSize );
    memcpy(&(ev->senderEntryID), entryID, sizeof(O_ENTRYID));

    memcpy(&(ev->entryID), tmpXPEid, sizeof(O_ENTRYID));
    ev->cbData            =  dataSize;
    memcpy(&(ev->data), idStrW, dataSize-1);
    ev->senderHWND = otlkcon_eventHwnd;

    ev->eventType = OTLKCON_EVENT_GET_ID;
    result = otlkcon_event_sendmessage( otlkcon_eventHwnd, otlkcon_eventHwnd, ev );

    return result;
}

HRESULT O_IContainer::DeleteRemoteMessage(O_IMessage *msg, int flags)
{
    HRESULT result = S_OK;
    LPSPropValue propResult;
    ULONG count = 0, dataSize = 0;
    char *idStrA = NULL;
    otlkcon_event_struct *ev;
    
    SizedSPropTagArray(3,tags);
    tags.cValues       = 1;
	tags.aulPropTag[0] = PR_OTLKCON_FOREIGN_ID;

    if( xpAccountEID == NULL )
    {
        // Get a transport provider.
        result = GetXPP();
        if( result != S_OK )
        {
            // Warn the user that XPP not available
            if( !(msgstore->logonFlags & LOGON_NO_DIALOG) )
            {
                MessageBoxW((HWND)msgstore->ulUIParam, 
                    L"The Transport Provider is not ready.  Please send/receive to continue this operation",
                                    L"No suitable transport provider found", 
                                    MB_ICONERROR|MB_OK );
            }

            {
                // Eventually the XPP will call us up.
                OTLKCON_ENTERCRITICALSECTION(&otlkcon_xpp_wait_set_cs, "GetXPP");
                otlkcon_xpp_wait_set[this->entryID] = this;
            }
        }

        // Queue message for delete.
        SPropValue tmpProp = {0};
        tmpProp.ulPropTag = PR_OTLKCON_PENDING_ACTIONS;
        tmpProp.Value.l   = OTLKCON_ACTION_DEL;
        result = msg->_IntSetProps(1, (LPSPropValue)&tmpProp, NULL);
        
		//as a result of notification contentsTable->HrUpdateObjectRow is being called inside
		//result = msg->SaveChanges(0);

        result = contentsTable->HrUpdateObjectRow(0, msg);
        if( result != S_OK )
        {
            OTLKCON_DEBUGBREAK;
        }

        return result;
    }

    result = msg->_IntGetProps((LPSPropTagArray)&tags, 0, &count, &propResult);
	switch( result )
	{
		case MAPI_W_ERRORS_RETURNED:
		case S_OK:			

            if( PROP_ID(propResult[0].ulPropTag) == PROP_ID(PR_OTLKCON_FOREIGN_ID) )
            {
                if( PROP_TYPE(propResult[0].ulPropTag) == PT_STRING8 )
                {
                    idStrA = _strdup(propResult[0].Value.lpszA);
                }
                else
                {
                    // Probably PT_ERROR
					idStrA = _strdup("");
//                    OTLKCON_DEBUGBREAK;
                }
            }
			break;

		default:
			//Error condition
            OTLKCON_DEBUGBREAK;
			break;
    }

    if( idStrA == NULL )
    {
        // We don't have an id, not good
        OTLKCON_DEBUGBREAK;
        return E_FAIL;
    }

    wchar_t *idStrW 
            = (wchar_t *)memObj->pMalloc( (ULONG)(strlen(idStrA)+1)*sizeof(wchar_t) );
    swprintf_s(idStrW, strlen(idStrA)+1, L"%S", idStrA);

    dataSize = (ULONG)(wcslen(idStrW)+1)*sizeof(wchar_t);
    ev = (otlkcon_event_struct *)memObj->pMalloc( sizeof(otlkcon_event_struct) 
                    + dataSize );
    memcpy(&(ev->senderEntryID), entryID, sizeof(O_ENTRYID));

    memcpy(&(ev->entryID), xpAccountEID, sizeof(O_ENTRYID));
    ev->cbData            =  dataSize;
    memcpy(&(ev->data[0]), idStrW, dataSize);
    ev->senderHWND = otlkcon_eventHwnd;

    ev->eventType = OTLKCON_EVENT_DEL_APPOINTMENTS;
    result = otlkcon_event_sendmessage( otlkcon_eventHwnd, otlkcon_eventHwnd, ev );

    return result;
}

O_IContainerAdviseSink::O_IContainerAdviseSink( O_IContainer *con )
{
    cRef = 0;
    container = con;
}

O_IContainerAdviseSink::~O_IContainerAdviseSink()
{
    ;
}

STDMETHODIMP O_IContainerAdviseSink::QueryInterface (REFIID riid, void **ppv)
{
    HRESULT result = S_OK;

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, L"inside O_IProp::QueryInterface. \
 {%0.8X-%0.4X-%0.4X-%0.2X%0.2X-%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X}\n",
        riid.Data1, riid.Data2, riid.Data3, riid.Data4[0], riid.Data4[1],
        riid.Data4[2], riid.Data4[3], riid.Data4[4], riid.Data4[5],
        riid.Data4[6], riid.Data4[7]);

    *ppv = NULL;

    if( memcmp(&riid, &IID_IUnknown, sizeof(IID)) &&
        memcmp(&riid, &IID_IMAPIAdviseSink, sizeof(IID)))
    {
        result = ResultFromScode(E_NOINTERFACE);
        return result;
    }

    AddRef();
    *ppv = (LPVOID)this;

    return result;
}

ULONG STDMETHODCALLTYPE O_IContainerAdviseSink::AddRef (void)
{
    cRef++;

    return cRef;
}

ULONG STDMETHODCALLTYPE O_IContainerAdviseSink::Release (void)
{
    return --cRef;
}

HRESULT O_IContainerAdviseSink::UpdateContentsTableRow( O_ENTRYID *msgEid, int flags )
{
    HRESULT result = S_OK;
    ULONG propCount = 0;
//    LPSPropValue propResult;
//    LPSPropValue rowProps;
    ULONG objType = 0;
    O_IMessage *msg = NULL;
    ULONG assocMsg = 0;
//    SRow row = {0};
    ULONG folderContCount = 0;

    // Can't use O_IContainer::OpenEntry(), the message may not be in
    // the contents table yet.  Hopefully, we've already checked that
    // we are the parent.
    result = container->msgstore->OpenEntry(sizeof(O_ENTRYID), (LPENTRYID)msgEid, 
                                        &IID_IMessage, 0, &objType, (LPUNKNOWN*)&msg);
    if( result != S_OK )
    {
        // This isn't good...
        OTLKCON_DEBUGBREAK;
        return result;
    }

    LPSPropValue lpspvFlags = NULL;
	if(SUCCEEDED(HrGetOneProp(msg, PR_MESSAGE_FLAGS, &lpspvFlags)))
	{
		if( lpspvFlags->Value.l & MSGFLAG_ASSOCIATED )
		{
			// Use the associated contents table
			assocMsg = 1;
		}
		container->memObj->lpFreeBuffer(lpspvFlags);
	} // if

    O_ITable *currContentsTable;
    if(assocMsg)
	{
		currContentsTable = container->assocContentsTable;
	}
	else
	{
		currContentsTable = container->contentsTable;
	}

    if( flags & OTLKCON_ACTION_DEL )
    {
        // Delete the appropriate row from the contents table
        // We're doing this here to disallow other file operations
        // from finding this object during delete.
        {
            OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs,
                                    "O_IContainerAdviseSink::UpdateContentsTableRow");
            SPropValue prop = {0};

            LPBYTE tmpGuid = (LPBYTE)container->memObj->malloc( sizeof(MAPIUID) );
            memcpy( tmpGuid, &(msgEid->instanceGuid), sizeof(MAPIUID) );
            prop.ulPropTag  = PR_INSTANCE_KEY;
            prop.Value.bin.lpb = tmpGuid;
            prop.Value.bin.cb  = sizeof(MAPIUID);
            
            result = currContentsTable->HrDeleteRow( &prop );
        }

        switch( result )
        {
            case MAPI_E_NOT_FOUND:
                // Some one beat us too it.
                return S_OK;

            case S_OK:
                break;

            default:
                OTLKCON_DEBUGBREAK;
                // An error occurred.
                return result;
        }

        // This update is due to a deleted object
//        if( container->isForeignView )
//	    {
//            
//			//what if we is in process of deleting this message
//			result = container->DeleteRemoteMessage( msg, 0 );
//            if( result != S_OK )
//            {
//                OTLKCON_DEBUGBREAK;
//                return result;
//            }
//        }
    }
    else
    {
        // Add or Modify

	    // FIXME: Not all row changes require an upload.  We need a
	    //        flag that differentiates.
	    if( container->isForeignView )
	    {
		    result = UpdateRemoteMessage(msg,flags);
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                return result;
            }
	    }
        
        result = currContentsTable->HrUpdateObjectRow(0, msg);
    }

    return result;
}

HRESULT O_IContainerAdviseSink::UpdateHierarchyTableRow( O_ENTRYID *folderEid, int flags )
{
	HRESULT result = S_OK;
	ULONG propCount = 0;
	ULONG objType = 0;
	O_IFolder *pFolder = NULL;

	// Can't use O_IContainer::OpenEntry(), the message may not be in
	// the contents table yet.  Hopefully, we've already checked that
	// we are the parent.
	result = container->msgstore->OpenEntry(sizeof(O_ENTRYID), (LPENTRYID)folderEid, &IID_IMAPIFolder, 
		0, &objType, (LPUNKNOWN*)&pFolder);
	if( result != S_OK )
	{
		// This isn't good...
		OTLKCON_DEBUGBREAK;
		return result;
	}

//	pFolder->Dump();

    if( flags & OTLKCON_ACTION_DEL )
    {
        // Delete the hierarchy table row for this object.
        SPropValue prop = {0};

        prop.ulPropTag  = PR_ENTRYID;
        prop.Value.bin.lpb = (LPBYTE)(pFolder->entryID);
        prop.Value.bin.cb  = sizeof(O_ENTRYID);

        result = container->hierarchyTable->HrDeleteRow( &prop );
        if( result != S_OK )
        {
            OTLKCON_DEBUGBREAK;
        }

        return result;
    }

	result = container->hierarchyTable->HrUpdateObjectRow(0, pFolder);
	
	return result;
}


ULONG O_IContainerAdviseSink::OnNotify( ULONG cNotif, LPNOTIFICATION lpNotifications)
{
    ULONG result = S_OK;
    LPNOTIFICATION currNotif;
    SRestriction rt;
    SPropertyRestriction cr;
    SPropValue prop;
    LPSRowSet rows = NULL;
    O_ENTRYID *tmpEid = NULL;
    int found;
    ULONG res = 0;

    for( ULONG i=0; i<cNotif; i++)
    {
        currNotif = lpNotifications+i;

        //Is this an event we care about?
        switch( currNotif->ulEventType)
        {
            case fnevObjectCreated:
                {
                    // Is this an object we care about?

                    // What type of object was created?
                    tmpEid = (O_ENTRYID*)(currNotif->info.obj.lpEntryID);

                    if( memcmp( &(tmpEid->typeGuid), 
                                    &otlkcon_imapifolder_guid, sizeof(MAPIUID)) == 0 )
                    {
                        
					    //We do that in modification notification
					    break;
					    // It's a folder...

                        // Is the parent in our hierarchy table ?
                        memset(&rt,0,sizeof(SRestriction));
                        rt.rt = RES_PROPERTY;
                        memset(&cr,0,sizeof(SPropertyRestriction));

                        prop.ulPropTag  = PR_ENTRYID;
                        prop.Value.bin.lpb = (LPBYTE)currNotif->info.obj.lpParentID;
                        prop.Value.bin.cb  = currNotif->info.obj.cbParentID;
                        cr.lpProp = &prop;
                        cr.relop = RELOP_EQ;
                        cr.ulPropTag = PR_ENTRYID;
                        rt.res.resProperty = cr;
                        found = 0;

                        result = container->hierarchyTable->FindRow(&rt, BOOKMARK_BEGINNING, 0);
                        switch(result)
                        {
                            case S_OK:
                            case MAPI_W_POSITION_CHANGED:
                                //Folder found, update its PR_SUBFOLDERS property.
                                result = container->hierarchyTable->QueryRows(1,0,&rows);
                                if( result != S_OK )
                                {
                                    OTLKCON_DEBUGBREAK;
                                }

                                if( rows->cRows < 1 )
                                {
                                    OTLKCON_DEBUGBREAK
                                }

                                // Search the row for the PR_SUBFOLDERS property
                                for( ULONG i=0; i < rows->aRow->cValues; i++ )
                                {
                                    if( rows->aRow->lpProps[i].ulPropTag == PR_SUBFOLDERS )
                                    {
                                        rows->aRow->lpProps[i].Value.b = TRUE;
                                        found = 1;
                                        break;
                                    }
                                }

                                if( found )
                                {
                                    // Save the modified row
                                    result 
                                        = container->hierarchyTable->HrModifyRow( 
                                                rows->aRow );
                                    if( result != S_OK )
                                    {
                                        OTLKCON_DEBUGBREAK;
                                    }
                                }
								if(NULL != rows)
								{
									FreeProws(rows);
									rows = NULL;
								} // if
                                break;

                            case MAPI_E_NOT_FOUND:
                                //Folder was not found
                                break;

                            default:
                                OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL,
                                    L"IMAPIContainer::OnNotify() : hierarchyTable->FindRow returned %X",
                                    result );
                                OTLKCON_DEBUGBREAK
                                break;
                        }
                    }
                    else if( memcmp( &(tmpEid->typeGuid), 
                                    &otlkcon_imessage_guid, sizeof(MAPIUID)) == 0 )
                    {
                        // It's a message...

                        // Are we the parent?
                        res = 0;
                        result = container->msgstore->CompareEntryIDs( sizeof(O_ENTRYID),
                                        (LPENTRYID)(container->entryID), sizeof(O_ENTRYID),
                                        currNotif->info.obj.lpParentID, 0, &res );

                        if( res )
                        {
                            //Add the message to our content's table
                            result = UpdateContentsTableRow( tmpEid, 
                                OTLKCON_ACTION_ADD );
                        }
                    }
                }
                break;

            case fnevObjectModified:
                {
                    tmpEid = (O_ENTRYID*)(currNotif->info.obj.lpEntryID);
                    if( memcmp( &(tmpEid->typeGuid), 
                        &otlkcon_imapifolder_guid, sizeof(MAPIUID)) == 0 )
                    {
                        // It's a folder...
					    // We need to update hierarchy table
					    // Are we the parent?
					    res = 0;
					    result = container->msgstore->CompareEntryIDs( sizeof(O_ENTRYID),
						    (LPENTRYID)(container->entryID), sizeof(O_ENTRYID),
						    currNotif->info.obj.lpParentID, 0, &res );

					    if( res )
					    {
						    //Modify the folders's hierarchy table row
						    result = UpdateHierarchyTableRow( tmpEid, 0 );
					    }
                    }
                    else if( memcmp( &(tmpEid->typeGuid), 
                        &otlkcon_imessage_guid, sizeof(MAPIUID)) == 0 )
                    {
                        // It's a message...

                        // Are we the parent?
                        res = 0;
                        result = container->msgstore->CompareEntryIDs( sizeof(O_ENTRYID),
                                        (LPENTRYID)(container->entryID), sizeof(O_ENTRYID),
                                        currNotif->info.obj.lpParentID, 0, &res );

                        if( res )
                        {
                            //Modify the message's contents table row
                            result = UpdateContentsTableRow( tmpEid, 
                                        OTLKCON_ACTION_MOD );
                        }
                    } 
                }
                break;

            case fnevObjectDeleted:
                {
                    tmpEid = (O_ENTRYID*)(currNotif->info.obj.lpEntryID);
                    if( memcmp( &(tmpEid->typeGuid), 
                        &otlkcon_imapifolder_guid, sizeof(MAPIUID)) == 0 )
                    {
                        // It's a folder...
					    // We need to update hierarchy table
					    // Are we the parent?
					    res = 0;
					    result = container->msgstore->CompareEntryIDs( sizeof(O_ENTRYID),
						    (LPENTRYID)(container->entryID), sizeof(O_ENTRYID),
						    currNotif->info.obj.lpParentID, 0, &res );

					    if( res )
					    {
						    //FIXME: Delete the folders's hierarchy table row
						    result = UpdateHierarchyTableRow( tmpEid, OTLKCON_ACTION_DEL );
					    }
                    }
                    else if( memcmp( &(tmpEid->typeGuid), 
                        &otlkcon_imessage_guid, sizeof(MAPIUID)) == 0 )
                    {
                        // It's a message...

                        // Are we the parent?
                        res = 0;
                        result = container->msgstore->CompareEntryIDs( sizeof(O_ENTRYID),
                                        (LPENTRYID)(container->entryID), sizeof(O_ENTRYID),
                                        currNotif->info.obj.lpParentID, 0, &res );

                        if( res )
                        {
                            //FIXME: Delete the message's contents table row
                            result = UpdateContentsTableRow( tmpEid, OTLKCON_ACTION_DEL );
                        }
                    }
                }
                break;

            default:
                OTLKCON_DEBUGBREAK
                break;
        }
    }


    return S_OK;
}

HRESULT O_IContainerAdviseSink::UpdateRemoteMessage(O_IMessage *msg, ULONG flags)
{
    HRESULT result = S_OK;

	//We don't need to do twice
	if(msg->IsUploading() || msg->IsDownloading()) 
	{
		return result;
	} // if
	
	result = msg->BeginUploading();
	if(FAILED(result))
	{
		return result;
	} // if

	if(!msg->HasForeignID()) 
	{
		//we need it there so we can find message by this ID in the future
		result = msg->CreateNewForeignID();
	} // if
	if(FAILED(result))
	{
		return result;
	} // if

    if( container->xpAccountEID == NULL )
    {
        // Get a transport provider.
        result = container->GetXPP();
        if( result != S_OK )
        {
            if( !(container->msgstore->logonFlags & LOGON_NO_DIALOG) )
            {
                MessageBoxW((HWND)container->msgstore->ulUIParam, 
                    L"The Transport Provider is not ready.  \
                     Please send/receive to continue this operation",
                                    L"No suitable transport provider found", 
                                    MB_ICONERROR|MB_OK );
            }

            {
                // Eventually the XPP will call us up.
                OTLKCON_ENTERCRITICALSECTION(&otlkcon_xpp_wait_set_cs, "GetXPP");
                otlkcon_xpp_wait_set[container->entryID] = container;
            }
        }

        // Queue message for add or update
        SPropValue tmpProp = {0};
        tmpProp.ulPropTag = PR_OTLKCON_PENDING_ACTIONS;
        tmpProp.Value.l   |= flags&OTLKCON_ACTION_ADD;
        tmpProp.Value.l   |= flags&OTLKCON_ACTION_MOD;

        result = msg->_IntSetProps(1, (LPSPropValue)&tmpProp, NULL);
        result = msg->SaveChanges(0);

        return result;
    }

    result = container->UpdateRemoteMessage(msg,flags);
    
    return result;
}
